﻿using NUnit.Framework;
using System.Text;

namespace Tests
{
    [TestFixture]
    public class Evals // http://redis.io/commands#eval
    {
        private const int DB = 3;

        [Test]
        public void Eval()
        {
            using (var conn = Config.GetUnsecuredConnection(waitForOpen:true))
            {   
                conn.Keys.Remove(DB, "evalkey");
                var script = @"return redis.call('set', KEYS[1], ARGV[1])";

                var task = conn.Evals.Eval(DB, script, new [] { "evalkey" }, new [] { "testvalue" });
                conn.Wait(task);

                var result = conn.Strings.GetString(DB, "evalkey");
                Assert.AreEqual("testvalue", conn.Wait(result));
            }
        }

        [Test]
        public void EvalBoolean()
        {
            using (var conn = Config.GetUnsecuredConnection(waitForOpen:true))
            {   
                conn.Keys.Remove(DB, "evalkey");
                conn.Strings.Set(DB, "evalkey", "abcdefghi");
                var script = @"return redis.call('exists', KEYS[1])";

                var task = conn.Evals.EvalBoolean(DB, script, new [] { "evalkey" }, new string[] {});
                var result = conn.Wait(task);

                Assert.IsTrue(result);
            }
        }

        [Test]
        public void EvalBytes()
        {
            using (var conn = Config.GetUnsecuredConnection(waitForOpen:true))
            {   
                conn.Keys.Remove(DB, "evalkey");
                conn.Strings.Set(DB, "evalkey", "abcdefghi");
                var script = @"return redis.call('get', KEYS[1])";

                var task = conn.Evals.EvalBytes(DB, script, new [] { "evalkey" }, new string[] {});
                var results = conn.Wait(task);

                Assert.AreEqual("abcdefghi", Decode(results));
            }
        }

        [Test]
        public void EvalString()
        {
            using (var conn = Config.GetUnsecuredConnection(waitForOpen:true))
            {   
                conn.Keys.Remove(DB, "evalkey");
                conn.Strings.Set(DB, "evalkey", "abcdefghi");
                var script = @"return redis.call('get', KEYS[1])";

                var task = conn.Evals.EvalString(DB, script, new [] { "evalkey" }, new string[] {});
                var result = conn.Wait(task);

                Assert.AreEqual("abcdefghi", result);
            }
        }

        [Test]
        public void EvalMultiString()
        {
            using (var conn = Config.GetUnsecuredConnection(waitForOpen:true))
            {   
                conn.Keys.Remove(DB, "evalkey");
                conn.Strings.Set(DB, "evalkey1", "abcdefghi");
                conn.Strings.Set(DB, "evalkey2", "jklmnopqr");
                var script = @"return redis.call('mget', KEYS[1], KEYS[2])";

                var task = conn.Evals.EvalMultiString(DB, script, new [] { "evalkey1", "evalkey2" }, new string[] {});
                var results = conn.Wait(task);

                Assert.AreEqual(2, results.Length);
                Assert.AreEqual("abcdefghi", results[0]);
                Assert.AreEqual("jklmnopqr", results[1]);
            }
        }

        [Test]
        public void EvalLong()
        {
            using (var conn = Config.GetUnsecuredConnection(waitForOpen:true))
            {   
                conn.Keys.Remove(DB, "evalkey");
                conn.Strings.Set(DB, "evalkey", "483749934");
                var script = @"return redis.call('get', KEYS[1])";

                var task = conn.Evals.EvalLong(DB, script, new [] { "evalkey" }, new string[] {});
                var result = conn.Wait(task);

                Assert.AreEqual(483749934, result);
            }
        }

        [Test]
        public void EvalHashPairs()
        {
            using (var conn = Config.GetUnsecuredConnection(waitForOpen:true))
            {   
                conn.Keys.Remove(DB, "hashkey");
                
                conn.Hashes.Set(DB, "hashkey", "foo", "abc");
                conn.Hashes.Set(DB, "hashkey", "bar", "def");

                var script = @"return redis.call('hgetall', KEYS[1])";

                var task = conn.Evals.EvalHashPairs(DB, script, new [] { "hashkey" }, new string[] {});
                var results = conn.Wait(task);

                Assert.AreEqual(2, results.Count);
                Assert.AreEqual("abc", Decode(results["foo"]));
                Assert.AreEqual("def", Decode(results["bar"]));
            }
        }

        [Test]
        public void EvalMultiBytes()
        {
            using (var conn = Config.GetUnsecuredConnection(waitForOpen:true))
            {   
                conn.Keys.Remove(DB, "hashkey");
                
                conn.Hashes.Set(DB, "hashkey", "foo", "abc");
                conn.Hashes.Set(DB, "hashkey", "bar", "def");

                var script = @"return redis.call('hvals', KEYS[1])";

                var task = conn.Evals.EvalMultiBytes(DB, script, new [] { "hashkey" }, new string[] {});
                var results = conn.Wait(task);

                Assert.AreEqual(2, results.Length);
                Assert.AreEqual("abc", Decode(results[0]));
                Assert.AreEqual("def", Decode(results[1]));
            }
        }

        static string Decode(byte[] value) { return Encoding.UTF8.GetString(value); }
    }
}
